package br.pucrio.inf.les.genarch.core.project.navigation.visitors;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
import org.eclipse.jdt.core.dom.NormalAnnotation;

import br.pucrio.inf.les.genarch.core.annotations.FeatureType;
import br.pucrio.inf.les.genarch.core.models.ArchitectureModelContent;
import br.pucrio.inf.les.genarch.core.models.FeatureModelContent;
import br.pucrio.inf.les.genarch.core.project.elements.AnnotationClassVisitor;
import br.pucrio.inf.les.genarch.core.project.navigation.NavigationUtil;
import br.pucrio.inf.les.genarch.core.project.navigation.ResourceClient;
import br.pucrio.inf.les.genarch.core.project.navigation.itens.FeatureItem;
import br.pucrio.inf.les.genarch.models.architecture.ArchitectureComponent;
import br.pucrio.inf.les.genarch.models.architecture.ArchitectureContainer;
import br.pucrio.inf.les.genarch.models.architecture.ArchitectureFolder;
import br.pucrio.inf.les.genarch.models.architecture.ArchitectureResourcesContainer;

public class ArchitectureModelResourceVisitor implements ResourceClient {

	private ArchitectureModelContent architectureContent;	

	public ArchitectureModelResourceVisitor() {
		this.architectureContent = new ArchitectureModelContent();
	}

	public void visit(IResource resource) {
		String resourceName = resource.getName();
		String resourcePath = resource.getProjectRelativePath().removeFirstSegments(1).toString();
		String resourcePhysicalPath = resource.getProjectRelativePath().toString();

		if ( resource.getProjectRelativePath().segmentCount() == 1 ) {	    
			if ( JavaCore.create(resource) instanceof IPackageFragmentRoot ) {
				this.architectureContent.addContainer(resource.getName());
			} else if ( resource instanceof IFolder ) {
				this.architectureContent.addResourceContainer(resource.getName());
			}
		} else if ( resource.getType() == IResource.FOLDER ) {
			IFolder component = (IFolder)resource;
			String parentPath = NavigationUtil.getParentByPath(component.getProjectRelativePath().toString());
			EObject parent = this.architectureContent.getElementByPath(parentPath);

			if ( parent instanceof ArchitectureContainer ) {		
				this.architectureContent.addComponent(resource.getName(),(ArchitectureContainer)parent,resourcePhysicalPath);	    	
			} else if ( parent instanceof ArchitectureComponent ){
				this.architectureContent.addSubComponent(resourceName,resourcePhysicalPath,(ArchitectureComponent)parent);
			} else if ( parent instanceof ArchitectureResourcesContainer ) {
				this.architectureContent.addFolder(resourceName,resourcePhysicalPath,(ArchitectureResourcesContainer)parent);
			} else if ( parent instanceof ArchitectureFolder ) {
				this.architectureContent.addSubFolder(resourceName,resourcePhysicalPath,(ArchitectureFolder)parent);
			}
		} else if ( resource.getType() == IResource.FILE ) {
			IFile file = (IFile)resource;
			String componentParentPath = NavigationUtil.getParentByPath(file.getProjectRelativePath().toString());
			EObject parent = architectureContent.getElementByPath(componentParentPath);

			if ( file.getFileExtension().equals("java") ) {
				this.architectureContent.addClass(resourceName,resourcePhysicalPath,(ArchitectureComponent)parent);
				//Colocar para varrer o arquivo e encontrar metodos e atributos anotados.
				EObject parentClass = architectureContent.getElementByPath(resourcePhysicalPath);
				for(FeatureItem item : getAnnotations(file).getFeatureItens()){
					this.architectureContent.addClass(item.getName(),resourcePhysicalPath,(ArchitectureComponent) parent );
				}
				

			} else if ( file.getFileExtension().equals("aj") ) {
				this.architectureContent.addAspect(resourceName,resourcePhysicalPath,(ArchitectureComponent)parent);
			} else if ( resource.getFileExtension().equals("xpt") ) {
				String genPath = resource.getProjectRelativePath().removeLastSegments(1).toString();
				if ( parent instanceof ArchitectureComponent ) {
					this.architectureContent.addTemplate(resourceName,resourcePhysicalPath,genPath,(ArchitectureComponent)parent);
				} else if ( parent instanceof ArchitectureFolder ) {
					this.architectureContent.addTemplate(resourceName,resourcePhysicalPath,genPath,(ArchitectureFolder)parent);
				}			
			} else if ( resource.getFileExtension().equals("mvel") ) {
				String toPath = resource.getProjectRelativePath().removeLastSegments(1).toString();
				this.architectureContent.addTemplate(resourceName,resourcePhysicalPath,toPath,(ArchitectureComponent)parent);
			} else {
				if ( parent instanceof ArchitectureResourcesContainer ) {
					this.architectureContent.addFile(resourceName,resourcePhysicalPath,(ArchitectureResourcesContainer)parent);
				} else if ( parent instanceof ArchitectureComponent ) {
					this.architectureContent.addFile(resourceName,resourcePhysicalPath,(ArchitectureComponent)parent);
				} else if ( parent instanceof ArchitectureFolder ) {
					this.architectureContent.addFile(resourceName,resourcePhysicalPath,(ArchitectureFolder)parent);
				}

			}
		}
	}

	private FeatureModelContent getAnnotations(IFile file) {	
		//this.featuresContent.getFeatureItens().addAll(JavaAnnotationUtil.featureAnnotations(file));
		FeatureModelContent featuresContent = new FeatureModelContent();
		ICompilationUnit compilationUnit = JavaCore.createCompilationUnitFrom(file);

		ASTParser astParser = ASTParser.newParser(AST.JLS3);
		astParser.setSource(compilationUnit);
		astParser.setKind(ASTParser.K_COMPILATION_UNIT);			
		astParser.setResolveBindings(true);
		ASTNode rootNode = astParser.createAST(new NullProgressMonitor());

		AnnotationClassVisitor visitor = new AnnotationClassVisitor();
		rootNode.accept(visitor);
		visitor.getSingleMemberAnnotations();
		visitor.getAnnotationsTypeDeclaration();
		visitor.getAnnotationsTypeMemberDeclaration();
		visitor.getMarkerAnnotations();
		for ( NormalAnnotation annotation : visitor.getNormalAnnotations() ) {
			IAnnotationBinding binding = annotation.resolveAnnotationBinding();

			if(annotation.isAnnotation()){
				if ( binding == null ) {
					continue;
				}
	
				if ( binding.getName().equals("Feature") ) {
					IMemberValuePairBinding[] membersValuePairBinding = binding.getDeclaredMemberValuePairs();
					FeatureItem featuresItem = new FeatureItem();
					for ( IMemberValuePairBinding memberValuePairBinding : membersValuePairBinding ) {								
						String name = memberValuePairBinding.getName();
						Object value = memberValuePairBinding.getValue();
						if ( "name".equals(name) ) {
							featuresItem.setName((String)value);
						} else if ( "type".equals(name) ) {
							IBinding variableBinding = (IBinding)value;									
							FeatureType featureType = FeatureType.valueOf(new String(variableBinding.getName()));
							featuresItem.setType(featureType);
						} else if ( "parent".equals(name) ) {
							featuresItem.setParent((String)value);
						}															
					}
					featuresContent.getFeatureItens().add(featuresItem);
				}
			}
		}		
		return featuresContent;
	}

	public ArchitectureModelContent getArchitectureContent() {
		return this.architectureContent;
	}	
}
